package com.kizuki.stream;

import org.junit.Test;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.LongStream;
import java.util.stream.Stream;

/**
 * debug 里面最后一个图标有 stream trace
 */
public class TestStream {

    private BiConsumer printMapInfo = (k, v) -> System.out.println("key:" + k + " value:" + v);

    /**
     * Stream 特性
     * Stream 自己不会存储元素。
     * Stream 不会改变源对象。相反，他们会返回一个持有结果的新Stream。
     * Stream 操作是延迟执行的。
     */
    @Test
    public void test(){
        List<String> strings = Arrays.asList("abc", "def", "gkh", "abc");
        Stream stream = strings.stream().filter(new Predicate() {
            @Override
            public boolean test(Object o) {
                System.out.println("Predicate.test 执行"); // 第二执行
                return true;
            }
        });

        System.out.println("count 执行"); // 先执行
        System.out.println(stream.count()); // 最后执行
    }

    @Test
    public void middleHandle() {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 65, 2);
        Stream<Integer> s1 = list.stream();
        Stream<Integer> s2 = list.stream();
        Stream<Integer> s3 = list.stream();
        Stream<Integer> s4 = list.stream();

        // [3, 4, 65, 2] 取后
        System.out.println(s1.skip(2).collect(Collectors.toList()));
        // [1, 2, 3] 取前
        System.out.println(s2.limit(3).collect(Collectors.toList()));
        // [1, 2, 3, 4, 65] 去重
        System.out.println(s3.distinct().collect(Collectors.toList()));
        // [map1, map2, map2, map3, map4]
        System.out.println(s4.filter(a -> a < 10).sorted().map(a -> "map" + a).collect(Collectors.toList()));

        System.out.println("原来的list = " + list);
    }

    @Test
    public void closed() {
        Stream<Integer> s1 = Stream.of(1, 2, 3, 4, 65);

        Stream<Integer> s2 = s1.filter(a -> a < 100).sorted();

        /**
         * java.lang.IllegalStateException: stream has already been operated upon or closed
         * 对于流的处理, s1 filter之后关闭了，可以理解为s1数据流向了s2
         */
        s2.map(a -> "map" + a);
        System.out.println("s2处理成功");

        try {
            s1.map(a -> "map" + a);
            System.out.println("s1处理成功");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("s1处理失败");
        } finally {
        }
    }


    @Test
    public void testSomeMethod() {
        List<User> users = Arrays.asList(new User(1), new User(2), new User(1), new User(666));
        /**
         * max min
         * parallelStream 并行流
         */
        Optional<User> max = users.parallelStream().max(Comparator.comparingInt(User::getLv));
        System.out.println("maxUser = " + max.orElse(null));

        List<User> usersCollect = users.stream().collect(Collectors.toList());
        System.out.println(Objects.equals(usersCollect.get(0), users.get(0))); // true
        System.out.println(Objects.equals(usersCollect, users)); // true 浅拷贝 若需要深拷贝用ByteArrayOutputStream

        /**
         * anyMatch、allMatch、oneMatch
         */
        System.out.println(Stream.of(1, 2, 3, 4, 65).allMatch(a -> a % 2 == 0));
    }

    /**
     * collect Collectors分流
     */
    @Test
    public void fenLiu() {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 65);
        Map<Boolean, List<Integer>> passingFailing = stream.collect(Collectors.partitioningBy(s -> s >= 60)); // 根据条件分流
        passingFailing.forEach(printMapInfo);

        Stream<User> s1 = Stream.of(new User(1), new User(2), new User(1));
        Map<Integer, List<User>> p1 = s1.collect(Collectors.groupingBy(User::getLv)); // 根据对象的某个属性分流
        p1.forEach(printMapInfo);
    }

    /**
     * collect Collectors收集方法
     */
    @Test
    public void simpleCollect() {
        Stream<String> stream = Stream.of("I", "am", "so", "happy");
        // Function.identity() t -> t
        // Collectors 还有toList toSet方法
        Map<String, String> map = stream.collect(Collectors.toMap(Function.identity(), s -> s + "_value"));
        map.forEach(printMapInfo);

        ArrayList<String> collect = Stream.of("I", "am", "so", "happy").collect(Collectors.toCollection(ArrayList::new));
        System.out.println(collect);

        System.out.println(Arrays.stream(new String[]{"I", "am", "so", "happy"}).collect(Collectors.joining(" ")));
    }

    /**
     * reduce
     */
    @Test
    public void simpleReduce() {
        Stream<String> stream = Stream.of("I", "am", "so", "happy");
        // Optional 可以为null的容器对象
        Optional<String> optional = stream.reduce((s1, s2) -> s1.length() > s2.length() ? s1 : s2);
        System.out.println(optional.get());
    }

    /**
     * map (1 -> 1)
     * flatMap (1 -> n)
     * parallel并行流
     */
    @Test
    public void parallelReduce() {
        Stream<List<String>> stream = Stream.of(Arrays.asList("i", "y"), Arrays.asList("ha", "wa"), Arrays.asList("aaa", "bbb"));

        /**
         * 第一个参数：返回实例u，传递你要返回的U类型对象的初始化实例u
         * 第二个参数：累加器accumulator，可以使用lambda表达式，声明你在u上累加你的数据来源t的逻辑，例如(u,t)->u.sum(t),此时lambda表达式的行参列表是返回实例u和遍历的集合元素t，函数体是在u上累加t
         * 第三个参数：参数组合器combiner
         * 可以理解数据初始为0， 并行相加accumulator，combiner则是将并行相加的结果再组合
         */
        Integer integer = stream.parallel()
                .flatMap(Collection::stream)
                .reduce(0, (sum, str) -> {
                    System.out.println("开始相加:" + str);
                    return sum + str.length();
                }, (a, b) -> {
                    int min = a + b;
                    System.out.println("并行计算暂时结果为" + min);
                    return min;
                }
        );
        System.out.println("最终结果为" + integer);
    }

    @Test
    public void parallelReduce2() {
        long start = System.currentTimeMillis();
        LongStream.rangeClosed(0L, 100_0000_0000L).parallel().reduce(Long::sum);
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }

    @Test
    public void summaryStatistics() {
        List<Integer> integers = Arrays.asList(1, 4, 66, 43, 3, 7);
        /**
         * mapToInt获得IntStream
         * 调用IntStream的summaryStatistics方法
         */
        IntSummaryStatistics statistics = integers.stream().mapToInt(x -> x).summaryStatistics();
        System.out.println(statistics.getSum());
        System.out.println(statistics.getAverage());
        System.out.println(statistics.getMax());
        System.out.println(statistics.getMin());
        System.out.println(statistics.getCount());
    }

    @Test
    public void testOptional() {
        User user = new User(1, "a");
        /**
         * old
         * user name = unknow
         */
        if (user != null && user.getName() != null) {
            System.out.println("user name = " + user.getName());
        } else {
            System.out.println("user name = unknow");
        }

        /**
         * new
         * Optional.empty
         * user name = unknow
         */
        System.out.println(Optional.of(user).map(User::getName));
        System.out.println(Optional.ofNullable(user).map(User::getName).orElse("user name = unknow"));

        try {
            System.out.println(Optional.of(null));
        } catch (Exception e) {
            System.out.println("java.lang.NullPointerException");
        }
    }

}
